"""Conversation management API routes."""

from typing import List, Optional, Dict, Any
from datetime import datetime, timedelta
import logging

from fastapi import APIRouter, Depends, HTTPException, Query, WebSocket, WebSocketDisconnect
from fastapi.responses import JSONResponse, StreamingResponse
from pydantic import BaseModel, Field
import motor.motor_asyncio

from ..auth.jwt_handler import get_current_user
from ..models.conversation_model import (
    ConversationStatus, ConversationCategory, MessageRole
)
from ..conversations import (
    ConversationManager,
    MessageHandler,
    ExportService,
    ShareService,
    TitleGenerator
)
from ..database import get_mongodb_client

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/v1/conversations", tags=["conversations"])


# Pydantic models for requests/responses

class ConversationCreate(BaseModel):
    """Create conversation request."""
    title: Optional[str] = None
    model_preference: str = "gpt-4"
    category: str = "其他"


class ConversationUpdate(BaseModel):
    """Update conversation request."""
    title: Optional[str] = None
    tags: Optional[List[str]] = None
    category: Optional[str] = None
    model_preference: Optional[str] = None
    settings: Optional[Dict[str, Any]] = None


class MessageCreate(BaseModel):
    """Create message request."""
    content: str
    role: str = "user"
    model: Optional[str] = None
    tokens_used: int = 0
    cost: float = 0.0


class MessageUpdate(BaseModel):
    """Update message request."""
    content: str


class ShareCreate(BaseModel):
    """Create share link request."""
    expires_in_hours: Optional[int] = 24
    password: Optional[str] = None


class TagUpdate(BaseModel):
    """Tag update request."""
    tags: List[str]


class ConversationResponse(BaseModel):
    """Standard conversation response."""
    success: bool
    data: Optional[Dict[str, Any]] = None
    message: Optional[str] = None
    error: Optional[str] = None


# Dependency injection

async def get_conversation_manager(
    mongodb_client: motor.motor_asyncio.AsyncIOMotorClient = Depends(get_mongodb_client)
) -> ConversationManager:
    """Get conversation manager instance."""
    return ConversationManager(mongodb_client)


async def get_message_handler(
    mongodb_client: motor.motor_asyncio.AsyncIOMotorClient = Depends(get_mongodb_client)
) -> MessageHandler:
    """Get message handler instance."""
    return MessageHandler(mongodb_client)


async def get_export_service(
    mongodb_client: motor.motor_asyncio.AsyncIOMotorClient = Depends(get_mongodb_client)
) -> ExportService:
    """Get export service instance."""
    return ExportService(mongodb_client)


async def get_share_service(
    mongodb_client: motor.motor_asyncio.AsyncIOMotorClient = Depends(get_mongodb_client)
) -> ShareService:
    """Get share service instance."""
    return ShareService(mongodb_client)


async def get_title_generator(
    mongodb_client: motor.motor_asyncio.AsyncIOMotorClient = Depends(get_mongodb_client)
) -> TitleGenerator:
    """Get title generator instance."""
    return TitleGenerator(mongodb_client)


# Conversation CRUD endpoints

@router.post("/", response_model=ConversationResponse)
async def create_conversation(
    request: ConversationCreate,
    current_user: Dict[str, Any] = Depends(get_current_user),
    manager: ConversationManager = Depends(get_conversation_manager)
):
    """Create a new conversation."""
    try:
        conversation_id = await manager.create_conversation(
            user_id=current_user["user_id"],
            title=request.title,
            model_preference=request.model_preference,
            category=ConversationCategory(request.category)
        )
        
        return ConversationResponse(
            success=True,
            data={"conversation_id": conversation_id},
            message="Conversation created successfully"
        )
    except Exception as e:
        logger.error(f"Failed to create conversation: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


@router.get("/", response_model=ConversationResponse)
async def list_conversations(
    skip: int = Query(0, ge=0),
    limit: int = Query(20, ge=1, le=100),
    status: Optional[str] = None,
    tags: Optional[List[str]] = Query(None),
    category: Optional[str] = None,
    search: Optional[str] = None,
    sort_by: str = "updated_at",
    sort_order: int = Query(-1, ge=-1, le=1),
    current_user: Dict[str, Any] = Depends(get_current_user),
    manager: ConversationManager = Depends(get_conversation_manager)
):
    """List user's conversations with pagination and filtering."""
    try:
        status_enum = ConversationStatus(status) if status else None
        category_enum = ConversationCategory(category) if category else None
        
        conversations, total = await manager.list_conversations(
            user_id=current_user["user_id"],
            skip=skip,
            limit=limit,
            status=status_enum,
            tags=tags,
            category=category_enum,
            search_query=search,
            sort_by=sort_by,
            sort_order=sort_order
        )
        
        return ConversationResponse(
            success=True,
            data={
                "conversations": [c.to_dict() for c in conversations],
                "total": total,
                "skip": skip,
                "limit": limit
            }
        )
    except Exception as e:
        logger.error(f"Failed to list conversations: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


@router.get("/{conversation_id}", response_model=ConversationResponse)
async def get_conversation(
    conversation_id: str,
    current_user: Dict[str, Any] = Depends(get_current_user),
    manager: ConversationManager = Depends(get_conversation_manager)
):
    """Get conversation details."""
    try:
        conversation = await manager.get_conversation(
            conversation_id=conversation_id,
            user_id=current_user["user_id"]
        )
        
        if not conversation:
            raise HTTPException(status_code=404, detail="Conversation not found")
        
        return ConversationResponse(
            success=True,
            data=conversation.to_dict()
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Failed to get conversation: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


@router.put("/{conversation_id}", response_model=ConversationResponse)
async def update_conversation(
    conversation_id: str,
    request: ConversationUpdate,
    current_user: Dict[str, Any] = Depends(get_current_user),
    manager: ConversationManager = Depends(get_conversation_manager)
):
    """Update conversation metadata."""
    try:
        category_enum = ConversationCategory(request.category) if request.category else None
        
        success = await manager.update_conversation(
            conversation_id=conversation_id,
            user_id=current_user["user_id"],
            title=request.title,
            tags=request.tags,
            category=category_enum,
            model_preference=request.model_preference,
            settings=request.settings
        )
        
        if not success:
            raise HTTPException(status_code=404, detail="Conversation not found")
        
        return ConversationResponse(
            success=True,
            message="Conversation updated successfully"
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Failed to update conversation: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


@router.delete("/{conversation_id}", response_model=ConversationResponse)
async def delete_conversation(
    conversation_id: str,
    hard_delete: bool = Query(False),
    current_user: Dict[str, Any] = Depends(get_current_user),
    manager: ConversationManager = Depends(get_conversation_manager)
):
    """Delete conversation (soft delete by default)."""
    try:
        success = await manager.delete_conversation(
            conversation_id=conversation_id,
            user_id=current_user["user_id"],
            hard_delete=hard_delete
        )
        
        if not success:
            raise HTTPException(status_code=404, detail="Conversation not found")
        
        return ConversationResponse(
            success=True,
            message="Conversation deleted successfully"
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Failed to delete conversation: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


@router.post("/{conversation_id}/archive", response_model=ConversationResponse)
async def archive_conversation(
    conversation_id: str,
    current_user: Dict[str, Any] = Depends(get_current_user),
    manager: ConversationManager = Depends(get_conversation_manager)
):
    """Archive a conversation."""
    try:
        success = await manager.archive_conversation(
            conversation_id=conversation_id,
            user_id=current_user["user_id"]
        )
        
        if not success:
            raise HTTPException(status_code=404, detail="Conversation not found")
        
        return ConversationResponse(
            success=True,
            message="Conversation archived successfully"
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Failed to archive conversation: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


@router.post("/{conversation_id}/restore", response_model=ConversationResponse)
async def restore_conversation(
    conversation_id: str,
    current_user: Dict[str, Any] = Depends(get_current_user),
    manager: ConversationManager = Depends(get_conversation_manager)
):
    """Restore an archived or soft-deleted conversation."""
    try:
        success = await manager.restore_conversation(
            conversation_id=conversation_id,
            user_id=current_user["user_id"]
        )
        
        if not success:
            raise HTTPException(status_code=404, detail="Conversation not found")
        
        return ConversationResponse(
            success=True,
            message="Conversation restored successfully"
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Failed to restore conversation: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


# Message management endpoints

@router.post("/{conversation_id}/messages", response_model=ConversationResponse)
async def add_message(
    conversation_id: str,
    request: MessageCreate,
    current_user: Dict[str, Any] = Depends(get_current_user),
    handler: MessageHandler = Depends(get_message_handler)
):
    """Add a message to conversation."""
    try:
        message = await handler.add_message(
            conversation_id=conversation_id,
            user_id=current_user["user_id"],
            content=request.content,
            role=MessageRole(request.role),
            model=request.model,
            tokens_used=request.tokens_used,
            cost=request.cost
        )
        
        if not message:
            raise HTTPException(status_code=404, detail="Conversation not found")
        
        return ConversationResponse(
            success=True,
            data=message.to_dict(),
            message="Message added successfully"
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Failed to add message: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


@router.get("/{conversation_id}/messages", response_model=ConversationResponse)
async def get_messages(
    conversation_id: str,
    skip: int = Query(0, ge=0),
    limit: int = Query(50, ge=1, le=200),
    current_user: Dict[str, Any] = Depends(get_current_user),
    handler: MessageHandler = Depends(get_message_handler)
):
    """Get messages from conversation."""
    try:
        messages = await handler.get_messages(
            conversation_id=conversation_id,
            user_id=current_user["user_id"],
            skip=skip,
            limit=limit
        )
        
        return ConversationResponse(
            success=True,
            data={
                "messages": [msg.to_dict() for msg in messages],
                "skip": skip,
                "limit": limit
            }
        )
    except Exception as e:
        logger.error(f"Failed to get messages: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


@router.put("/{conversation_id}/messages/{message_id}", response_model=ConversationResponse)
async def update_message(
    conversation_id: str,
    message_id: str,
    request: MessageUpdate,
    current_user: Dict[str, Any] = Depends(get_current_user),
    handler: MessageHandler = Depends(get_message_handler)
):
    """Update a message content."""
    try:
        success = await handler.update_message(
            conversation_id=conversation_id,
            user_id=current_user["user_id"],
            message_id=message_id,
            new_content=request.content
        )
        
        if not success:
            raise HTTPException(status_code=404, detail="Message not found")
        
        return ConversationResponse(
            success=True,
            message="Message updated successfully"
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Failed to update message: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


@router.delete("/{conversation_id}/messages/{message_id}", response_model=ConversationResponse)
async def delete_message(
    conversation_id: str,
    message_id: str,
    current_user: Dict[str, Any] = Depends(get_current_user),
    handler: MessageHandler = Depends(get_message_handler)
):
    """Delete a message from conversation."""
    try:
        success = await handler.delete_message(
            conversation_id=conversation_id,
            user_id=current_user["user_id"],
            message_id=message_id
        )
        
        if not success:
            raise HTTPException(status_code=404, detail="Message not found")
        
        return ConversationResponse(
            success=True,
            message="Message deleted successfully"
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Failed to delete message: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


# Search endpoints

@router.get("/search", response_model=ConversationResponse)
async def search_conversations(
    q: str = Query(..., description="Search query"),
    skip: int = Query(0, ge=0),
    limit: int = Query(20, ge=1, le=100),
    current_user: Dict[str, Any] = Depends(get_current_user),
    manager: ConversationManager = Depends(get_conversation_manager)
):
    """Search conversations by content."""
    try:
        conversations, total = await manager.search_conversations(
            user_id=current_user["user_id"],
            query=q,
            skip=skip,
            limit=limit
        )
        
        return ConversationResponse(
            success=True,
            data={
                "conversations": [c.to_dict() for c in conversations],
                "total": total,
                "query": q,
                "skip": skip,
                "limit": limit
            }
        )
    except Exception as e:
        logger.error(f"Failed to search conversations: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


# Export endpoints

@router.get("/{conversation_id}/export")
async def export_conversation(
    conversation_id: str,
    format: str = Query("json", pattern="^(json|markdown)$"),
    current_user: Dict[str, Any] = Depends(get_current_user),
    export_service: ExportService = Depends(get_export_service)
):
    """Export conversation in specified format."""
    try:
        if format == "json":
            content, filename = await export_service.export_json(
                conversation_id=conversation_id,
                user_id=current_user["user_id"]
            )
            media_type = "application/json"
        else:  # markdown
            content, filename = await export_service.export_markdown(
                conversation_id=conversation_id,
                user_id=current_user["user_id"]
            )
            media_type = "text/markdown"
        
        return StreamingResponse(
            content,
            media_type=media_type,
            headers={
                "Content-Disposition": f"attachment; filename={filename}"
            }
        )
    except Exception as e:
        logger.error(f"Failed to export conversation: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# Share endpoints

@router.post("/{conversation_id}/share", response_model=ConversationResponse)
async def create_share_link(
    conversation_id: str,
    request: ShareCreate,
    current_user: Dict[str, Any] = Depends(get_current_user),
    share_service: ShareService = Depends(get_share_service)
):
    """Generate sharing link for conversation."""
    try:
        expires_at = None
        if request.expires_in_hours:
            expires_at = datetime.utcnow() + timedelta(hours=request.expires_in_hours)
        
        share_id = await share_service.create_share_link(
            conversation_id=conversation_id,
            user_id=current_user["user_id"],
            expires_at=expires_at,
            password=request.password
        )
        
        if not share_id:
            raise HTTPException(status_code=404, detail="Conversation not found")
        
        return ConversationResponse(
            success=True,
            data={
                "share_id": share_id,
                "share_url": f"/shared/conversations/{share_id}"
            },
            message="Share link created successfully"
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Failed to create share link: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


@router.delete("/{conversation_id}/share", response_model=ConversationResponse)
async def revoke_share_link(
    conversation_id: str,
    current_user: Dict[str, Any] = Depends(get_current_user),
    share_service: ShareService = Depends(get_share_service)
):
    """Revoke sharing link for conversation."""
    try:
        success = await share_service.revoke_share_link(
            conversation_id=conversation_id,
            user_id=current_user["user_id"]
        )
        
        if not success:
            raise HTTPException(status_code=404, detail="Conversation not found")
        
        return ConversationResponse(
            success=True,
            message="Share link revoked successfully"
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Failed to revoke share link: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


# Tag management endpoints

@router.post("/{conversation_id}/tags", response_model=ConversationResponse)
async def add_tags(
    conversation_id: str,
    request: TagUpdate,
    current_user: Dict[str, Any] = Depends(get_current_user),
    manager: ConversationManager = Depends(get_conversation_manager)
):
    """Add tags to conversation."""
    try:
        # Get current conversation
        conversation = await manager.get_conversation(
            conversation_id=conversation_id,
            user_id=current_user["user_id"]
        )
        
        if not conversation:
            raise HTTPException(status_code=404, detail="Conversation not found")
        
        # Merge tags
        new_tags = list(set(conversation.tags + request.tags))
        
        success = await manager.update_conversation(
            conversation_id=conversation_id,
            user_id=current_user["user_id"],
            tags=new_tags
        )
        
        return ConversationResponse(
            success=True,
            data={"tags": new_tags},
            message="Tags added successfully"
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Failed to add tags: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


@router.delete("/{conversation_id}/tags/{tag}", response_model=ConversationResponse)
async def remove_tag(
    conversation_id: str,
    tag: str,
    current_user: Dict[str, Any] = Depends(get_current_user),
    manager: ConversationManager = Depends(get_conversation_manager)
):
    """Remove a tag from conversation."""
    try:
        # Get current conversation
        conversation = await manager.get_conversation(
            conversation_id=conversation_id,
            user_id=current_user["user_id"]
        )
        
        if not conversation:
            raise HTTPException(status_code=404, detail="Conversation not found")
        
        # Remove tag
        new_tags = [t for t in conversation.tags if t != tag]
        
        success = await manager.update_conversation(
            conversation_id=conversation_id,
            user_id=current_user["user_id"],
            tags=new_tags
        )
        
        return ConversationResponse(
            success=True,
            data={"tags": new_tags},
            message="Tag removed successfully"
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Failed to remove tag: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


@router.get("/tags", response_model=ConversationResponse)
async def get_user_tags(
    current_user: Dict[str, Any] = Depends(get_current_user),
    manager: ConversationManager = Depends(get_conversation_manager)
):
    """Get all unique tags used by user."""
    try:
        tags = await manager.get_user_tags(current_user["user_id"])
        
        return ConversationResponse(
            success=True,
            data={"tags": tags}
        )
    except Exception as e:
        logger.error(f"Failed to get user tags: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


# Statistics endpoint

@router.get("/stats", response_model=ConversationResponse)
async def get_statistics(
    current_user: Dict[str, Any] = Depends(get_current_user),
    manager: ConversationManager = Depends(get_conversation_manager)
):
    """Get user's conversation statistics."""
    try:
        stats = await manager.get_statistics(current_user["user_id"])
        
        return ConversationResponse(
            success=True,
            data=stats
        )
    except Exception as e:
        logger.error(f"Failed to get statistics: {e}")
        return ConversationResponse(
            success=False,
            error=str(e)
        )


# WebSocket endpoint for real-time messaging

@router.websocket("/ws/{conversation_id}")
async def websocket_endpoint(
    websocket: WebSocket,
    conversation_id: str,
    handler: MessageHandler = Depends(get_message_handler)
):
    """WebSocket endpoint for real-time message updates."""
    await handler.connect_websocket(websocket, conversation_id)
    
    try:
        while True:
            # Keep connection alive and handle incoming messages
            data = await websocket.receive_text()
            # Handle incoming WebSocket messages if needed
            # For now, just keep the connection open
    except WebSocketDisconnect:
        await handler.disconnect_websocket(websocket, conversation_id)


# Shared conversation access (public endpoint)

shared_router = APIRouter(prefix="/api/v1/shared", tags=["shared"])


@shared_router.get("/conversations/{share_id}")
async def get_shared_conversation(
    share_id: str,
    password: Optional[str] = Query(None),
    share_service: ShareService = Depends(get_share_service)
):
    """Access shared conversation (no auth required)."""
    try:
        conversation = await share_service.get_shared_conversation(
            share_id=share_id,
            password=password
        )
        
        if not conversation:
            raise HTTPException(status_code=404, detail="Shared conversation not found or expired")
        
        return JSONResponse(
            status_code=200,
            content={
                "success": True,
                "data": conversation.to_dict()
            }
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Failed to get shared conversation: {e}")
        raise HTTPException(status_code=500, detail=str(e))